package gamagora.game
{
	import Box2D.Collision.Shapes.b2MassData;
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Collision.b2AABB;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2Fixture;
	
	import gamagora.box2d.OSPhysicBox;
	import gamagora.box2d.OSPhysicData;
	import gamagora.box2d.OSPhysicObject;
	import gamagora.box2d.OSPhysicSprite;
	import gamagora.box2d.OSPhysicWorld;
	import gamagora.game.utils.OSImages;
	import gamagora.game.utils.OSPlayerState;
	
	import org.flixel.FlxG;
	import org.flixel.FlxGroup;
	import org.flixel.FlxObject;
	import org.flixel.FlxSprite;
	import org.flixel.FlxState;
	import org.flixel.system.FlxAnim;
	
	public class OSPlayer extends OSPhysicBox
	{
		private static const WIDTH:Number = 20.0;
		private static const HEIGHT:Number = 20.0;
		private static const SPEED:Number = 20.0;
		private static const JUMP:Number = 150;
		private static const WALL_JUMP:Number = 50;

		private var _direction:b2Vec2;
		private var _vel:b2Vec2;
		private var _physHalfHeight:Number;
		private var _bodyMassData:b2MassData;

		private var _halfWidth:Number;
		
		//Collision
		private var _nbPlayerContacts:uint;
		private var _jumpDirection:int; 
		private var _canJump:Boolean	// Peux sauter
		private var _wallJump:Boolean	// Peux effectuer ou effectue un saut mural
		private var _onGround:Boolean;
		private var _onFire:Boolean;	// Marche sur du feu

		//Animation
		private var _state:String;
		private var _jumpState:String;
		private var _justJump:Boolean; 	// Lancement phase de saut  (utile pour le delai de saut) 
		private var _isJumping:Boolean; // Est dans la phase de saut
		private var _startRunL:Boolean; // Lancement phase de course gauche
		private var _startRunR:Boolean; // Lancement phase de course droit
		private var _isRunning:Boolean; // Est dans la phase de course
		private var _isExtJump:Boolean;
		
		// DEBUG
		private const DEBUG:Boolean = false;

		public function OSPlayer(x:Number=0, y:Number=0)
		{
			super(x, y, WIDTH, HEIGHT, 20, 20);
			
			if (!DEBUG)
			{	
				physicWidth = 30;
				physicHeight = 60;
				
				// TODO 0.4 sur sprite de base ??
				/*this.scale.x *= 0.4;
				this.scale.y *= 0.4;*/
				
				this.offset.x += 49;
				this.offset.y += 49;
				// Affichage
				loadGraphic(OSImages.MONKEY, true, true, 128, 128);
				
				// Animation
				addAnimation(OSPlayerState.IDLE, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], 10);
				addAnimation(OSPlayerState.RUN, [85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108], 20);
				addAnimation(OSPlayerState.WALL, [127]);
				
				// Jump 1
				addAnimation(OSPlayerState.JUMP1_TAKEOFF, [60, 61, 62, 63, 66, 67], 25, false);
				addAnimation(OSPlayerState.JUMP1_UP, [68]);
				addAnimation(OSPlayerState.JUMP1_TOP, [69, 70, 71, 72, 73], 30, false);
				addAnimation(OSPlayerState.JUMP1_DOWN, [74, 75]);
				addAnimation(OSPlayerState.JUMP1_LAND, [76, 77, 78, 79, 80, 81, 82, 83, 84], 20, false);
				
				// Jump 2 extension
				addAnimation(OSPlayerState.JUMP2_TAKEOFF, [135, 136, 137, 138, 139, 140, 141], 25, false);
				addAnimation(OSPlayerState.JUMP2_UP, [142, 143], 10);
				addAnimation(OSPlayerState.JUMP2_TOP, [144, 145], 20);
				addAnimation(OSPlayerState.JUMP2_DOWN, [146, 147, 148], 10);
				addAnimation(OSPlayerState.JUMP2_LAND, [149, 150, 151, 152, 153, 154], 20, false);
				
				// Run transitions
				addAnimation(OSPlayerState.IDLE_TO_RUN, [20, 21, 22, 23, 34, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38], 60, false);
				addAnimation(OSPlayerState.RUN_TO_IDLE, [40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58], 20, false);
				
				// Callback
				addAnimationCallback(animCallback);
				
				_halfWidth = this.width/2;
				
				_physHalfHeight = (HEIGHT / OSPhysicWorld.ratio) / 2;
				_nbPlayerContacts = 0;
				_jumpDirection = 0;
				_wallJump = false;
				_canJump = false;
				_justJump = false;
				_startRunL = false;
				_startRunR = false;
				_isJumping = true;
				_isRunning = false;
				
				_onFire = false;
				
				//Physique
				_physic.bodyDef.allowSleep = false;
				_physic.bodyDef.type = b2Body.b2_dynamicBody;
				_physic.bodyDef.fixedRotation = true;
				_physic.fixDef.density = 10.0;
				_physic.fixDef.friction = 0.5;
				
				// Useless ???
				_bodyMassData = new b2MassData();
				_bodyMassData.mass = 20;
				
				setData(OSPhysicData.PLAYER);
				
				_direction = new b2Vec2();
				
				
				facing = FlxObject.LEFT;
			} 
			else 
			{ 
				// DEBUG
				loadGraphic(OSImages.DEBUGMONKEY, true, true, WIDTH, HEIGHT);
				
				_halfWidth = WIDTH / 2;
				
				_physHalfHeight = (HEIGHT / OSPhysicWorld.ratio) / 2;
				_nbPlayerContacts = 0;
				_jumpDirection = 0;
				_wallJump = false;
				_canJump = false;
				
				//Physique
				_physic.bodyDef.type = b2Body.b2_dynamicBody;
				_physic.bodyDef.fixedRotation = true;
				_physic.fixDef.friction = 0.5;
				_physic.fixDef.density = 0.5;
				_physic.fixDef.restitution = 0;
				
				setData(OSPhysicData.PLAYER);
				
				_direction = new b2Vec2();
				
				//animations
				addAnimation(OSPlayerState.IDLE, [00, 01, 02, 03, 04, 05, 06, 07], 10);
				addAnimation(OSPlayerState.RUN,  [08, 09, 10, 11, 12, 13, 14, 15], 10);
				addAnimation(OSPlayerState.JUMP, [16, 17, 18, 19, 20, 21, 22, 23], 10);
				addAnimation(OSPlayerState.WALL, [24, 25, 26, 27, 28, 29, 30, 31], 10);
				
			}
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Fixture;

import gamagora.box2d.OSPhysicData;
import gamagora.box2d.OSPhysicSprite;
import gamagora.game.utils.OSPlayerState;

		}

		public override function create(group:FlxGroup):void
		{
			super.create(group);
			_physic.body.SetBullet(true);
			_physic.body.SetMassData(_bodyMassData);
			
		}

		public override function update():void
		{
			super.update();

			// Mise a jour de la vélocité 
			_vel = _physic.body.GetLinearVelocity();
			
			
			
			// Saut
			if((FlxG.keys.justPressed("UP")||_justJump) && _canJump) // Si on à le droit de sauter
			{
				_onGround = false;
				_isExtJump = _vel.x != 0; // Saut droit ou en extension
				if (!_isJumping && !_wallJump)
				{
					_justJump = true;
				} 
				else 
				{
					_justJump = false;
					_direction.Set(_jumpDirection*WALL_JUMP*SPEED, -JUMP); //On calcule la direction de saut  
					getBody().ApplyImpulse(_direction, getBody().GetPosition()) // On saute	
				}
				
			}
		
			// Control horizontal
			if(FlxG.keys.LEFT || _startRunL) // Gauche
			{
				_startRunR = false;
				if (_canJump && !_wallJump){
					if (!_isRunning){
						_startRunL = true;
					} else {
						_startRunL = false;
						_vel.x = -SPEED; // mouvement vers la gauche
					}
				}
				else if (_canJump && _wallJump && _jumpDirection == 1)
				{
					// LEFT DESACTIVE MOUAHAHAHAHA
				}
				else if (!_canJump && _wallJump)
				{
					// On est en train de sauter d'un mur : control aerien limite
					if (_vel.x < 0)
					{ 
						_vel.x *= 1.01;
					} 
					else if (_vel.x == 0) 
					{
						_vel.x -= 1;
					} 
					else 
					{
						_vel.x *= 0.99;
					}
				}
				else 
				{
					_vel.x = -SPEED; // mouvement vers la gauche
				} 
			}
			if(FlxG.keys.RIGHT || _startRunR) // Droite
			{
				_startRunL = false;
				if (_canJump && !_wallJump){
					if (!_isRunning){
						_startRunR = true;
					} else {
						_startRunR = false;
						_vel.x = SPEED; // mouvement vers la droite
					}
				}
				else if (_canJump && _wallJump && _jumpDirection == -1)
				{
					// RIGHT DESACTIVE MOUAHAHAHAHA
				}
				else if (!_canJump && _wallJump)
				{
					// On est en train de sauter d'un mur : control aerien limite
					if (_vel.x>0)
					{
						_vel.x *= 1.01;
					} 
					else if (_vel.x == 0) 
					{
						_vel.x += 1;
					} 
					else 
					{
						_vel.x *= 0.99;
					}
				}
				else 
				{
					_vel.x = SPEED; // mouvement vers la droite
				}
			}
			
			
			//Linear damping seulement si le player touche le sol
			if(_canJump){
				if(_vel.x < 0)
				{
					_vel.x += 0.1;
					if(_vel.x > 0){
						_vel.x = 0;
					}
				}
				else if(_vel.x > 0)
				{
					_vel.x -= 0.1;
					if(_vel.x < 0){
						_vel.x = 0;
					}
				}
			}
			
			// Borne de vitesse
			//Horizontal
			if (_vel.x > 8)
			{
				_vel.x = 8;
			}
			else if (_vel.x < -8) 
			{
				_vel.x = -8;
			}
			// Vertical
			if(_vel.y < -10)
			{
				_vel.y = -10;
			} 
			else if (_vel.y > 0 && _vel.y < 15){
				getBody().ApplyImpulse(new b2Vec2(0 ,5), getBody().GetPosition());
			} 

			// Direction du regard (Inversé à cause des graphistes :p ... )
			if (_vel.x < 0)
			{
				facing = FlxObject.RIGHT;
			}
			else if (_vel.x > 0)
			{
				facing = FlxObject.LEFT;
			}
			
			// Le feu brule les pattes
			if(_onFire)
			{
				getBody().ApplyImpulse(new b2Vec2(uint(Math.round(Math.random())) == 0 ? -0.4 : 0.4, -0.2), getBody().GetPosition());
			}

			// Changement d'etat
			if(_vel.y != 0)
			{
				if (_wallJump && _canJump) // Wall
				{ 
					_state = OSPlayerState.WALL;
					_vel.y *= 0.95; // Accroche sur le mur
				} 
				else if (!_canJump)
				{
					_state = OSPlayerState.JUMP;
					if (_vel.y <-0.2) // Phase montante
					{
						_jumpState = (_isExtJump ? OSPlayerState.JUMP2_UP : OSPlayerState.JUMP1_UP);
					} 
					else if (_vel.y > -1 && _vel.y < 2) // Haut du saut
					{
						_jumpState = (_isExtJump ? OSPlayerState.JUMP2_TOP : OSPlayerState.JUMP1_TOP);
						
					} 
					else if (_vel.y > 0.2) // Phase descendante
					{
						_jumpState = (_isExtJump ? OSPlayerState.JUMP2_DOWN : OSPlayerState.JUMP1_DOWN);
					}
				}
			}
			else if (_justJump) // Demarage du saut
			{
				_state = OSPlayerState.JUMP;
				_jumpState = (_isExtJump ? OSPlayerState.JUMP2_TAKEOFF : OSPlayerState.JUMP1_TAKEOFF);
			} 
			else if (_startRunL || _startRunR) // Demarage de la course
			{
				_state = OSPlayerState.IDLE_TO_RUN;
			}
			else if(_vel.x == 0)
			{
				if(_isJumping) // Atterissage
				{
					_jumpState = (_isExtJump ? OSPlayerState.JUMP2_LAND : OSPlayerState.JUMP1_LAND);
				} 
				if (_isRunning) // Fin de course
				{
					_state = OSPlayerState.RUN_TO_IDLE;
				} 
				else // Idle
				{
					_state = OSPlayerState.IDLE;
				}
			}
			else 
			{
				_isJumping = false;
				//if (_isRunning) // En course
				//{
					_state = OSPlayerState.RUN;	
				//}
			}
			
			
			//Animation
			switch(_state)
			{
				case OSPlayerState.JUMP:
				{
					play(_jumpState);
					break;
				}
				default:
				{
					play(_state);
					break
				}
			}
			
			// DEBUG
			//trace("Vel : " + _vel.y);
			
		}
		
		private function animCallback(animationName:String, currentFrame:uint, currentFrameIndex:uint):void
		{
			var currentFlxAnim:FlxAnim = _curAnim;
			if ((animationName == OSPlayerState.JUMP1_TAKEOFF || animationName == OSPlayerState.JUMP2_TAKEOFF) 
				&& currentFrame >= currentFlxAnim.frames.length-1)
			{
				_isJumping = true;
			}
			else if((animationName == OSPlayerState.JUMP1_LAND || animationName == OSPlayerState.JUMP2_LAND)
				&& currentFrame >= currentFlxAnim.frames.length-1)
			{
				_isJumping = false;	
			}
			else if(animationName == OSPlayerState.IDLE_TO_RUN && currentFrame >= currentFlxAnim.frames.length-1)
			{
				_isRunning = true;	
			}
			else if(animationName == OSPlayerState.RUN_TO_IDLE && currentFrame >= currentFlxAnim.frames.length-1)
			{
				_isRunning = false;	
			}
		}
		
		//Collisions
		public override function onBeginContact(sprite:OSPhysicSprite):void
		{
			_nbPlayerContacts++;
			if(sprite.data.type == OSPhysicData.BOUNCE || sprite.data.type == OSPhysicData.GROUND)
			{
				_wallJump = false;
			} 
			
			if(sprite.data.type == OSPhysicData.FIRE_GROUND)
			{
				_onFire = true;
			} else if (sprite.data.type == OSPhysicData.FRUIT)
			{
				// NOOOPE CHUCK TESTA ! 
			}
			else
			{
				jumpControl(sprite);
			}
		}
		
		
		public override function onEndContact(sprite:OSPhysicSprite):void
		{
			_nbPlayerContacts--;
			_canJump = _nbPlayerContacts > 0;
			
			if(sprite.data.type == OSPhysicData.FIRE_GROUND)
			{
				_onFire = false;
			}
		}
		
		private function jumpControl(sprite:OSPhysicSprite):void
		{
			var obstacle:b2Fixture = sprite.getPhysic().fixture;
			
			//angle = sprite.angle;
			//trace("angles : me = " + angle + " sprite : " + sprite.angle);
			
			_wallJump = false;
			
			// Le joueur est posé sur l'obstacle
			if(getBody().GetPosition().y < obstacle.GetBody().GetPosition().y - obstacle.GetAABB().GetExtents().y || sprite.angle != 0)
			{
				//On peut donc sauter en hauteur
				_canJump = true;
				_wallJump = false;
				_jumpDirection = 0;
				_onGround = true;
				
			}
			else // Le joueur touche l'obstacle mais n'est pas au dessus
			{
				var obsData:OSPhysicData = obstacle.GetUserData();
				
				if (!_onGround) // Pas de WJ dans les coins de map 
				{
					if(obsData != null)
					{
						// Est ce que l'on est à gauche de l'obstacle ?
						if(getBody().GetPosition().x < obstacle.GetBody().GetPosition().x)
						{
							//Si oui on rebondit vers la gauche
							_canJump = true;
							_wallJump = true;
							_jumpDirection = -1;
						}
						else //On est à droite de l'obstacle
						{
							//On rebondit donc vers la droite
							_canJump = true;
							_wallJump = true;
							_jumpDirection = 1;
						}
					}
				}
			}
		}
		
		//Getters Setters
		public function get physHalfHeight():Number
		{
			return _physHalfHeight;
		}

		public function get halfWidth():Number
		{
			return _halfWidth;
		}

	}
}


/*/ TODO /// 

jump en atterissant = power jump ...

jump au bord d'un mur = THRILLER

jump au bord d'un creux

pente a gerer

*/


